#pragma once
#include <Mswsock.h>

class CSockAddr
{
public:
	CSockAddr()
		:m_addr_in((sockaddr_in&)m_addr)
	{
		ZeroMemory(&m_addr,iNameLen);
	}

	operator const sockaddr*() const
	{
		return &m_addr;
	}

	operator sockaddr*()
	{
		return &m_addr;
	}

	operator sockaddr_in&()
	{
		return m_addr_in;
	}

	short &sin_family()
	{
		return m_addr_in.sin_family;
	}

	u_short &sin_port()
	{
		return m_addr_in.sin_port;
	}

	in_addr &sin_addr()
	{
		return m_addr_in.sin_addr;
	}

	static int namelen()
	{
		return iNameLen;
	}
protected:
	sockaddr m_addr;
	sockaddr_in &m_addr_in;
	static const int iNameLen;
};
__declspec(selectany) const int CSockAddr::iNameLen=sizeof(sockaddr);

class CASYNCSocket
{
public:
	CASYNCSocket()
		:m_sock(INVALID_SOCKET)
	{
		InitFun();
		ASSERT(NULL!=lpConnectEx);
	}

	virtual ~CASYNCSocket()
	{
		CloseSocket();
	}

	virtual BOOL CreateSocket()
	{
		ASSERT(INVALID_SOCKET==m_sock);
		m_sock=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,NULL,WSA_FLAG_OVERLAPPED);
		return(INVALID_SOCKET!=m_sock);
	}

	virtual BOOL ConnectEx(u_long ulAddr, u_short uPort,LPOVERLAPPED lpOL,PVOID lpSendBuffer=NULL,DWORD dwSendBytes=0u,LPDWORD lpByteSent=NULL)
	{
		ASSERT(INVALID_SOCKET!=m_sock);
//		ASSERT(NULL!=lpSendBuffer);
		if(lpConnectEx)
		{
			CSockAddr addr;
			addr.sin_addr().s_addr=ulAddr;
			addr.sin_family()=AF_INET;
			addr.sin_port()=htons(uPort);
			
			return lpConnectEx(m_sock,addr,CSockAddr::namelen(),lpSendBuffer,dwSendBytes,lpByteSent,lpOL);
		}
		else
		{
			return FALSE;
		}
		return TRUE;
	}

	virtual int RecvEx(LPWSABUF lpBuffers,DWORD dwBufferCount,LPOVERLAPPED lpOL
		,LPDWORD lpdwFlags=NULL
		,LPDWORD lpdwBytesRecv=NULL		
		,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine=NULL)
	{
		ASSERT(INVALID_SOCKET!=m_sock);
		ASSERT(NULL!=lpOL);
		ASSERT(NULL!=lpBuffers);
		DWORD dwBytesRecv=0;
		DWORD dwFlags=0;
		if(NULL==lpdwBytesRecv)
		{
			lpdwBytesRecv=&dwBytesRecv;
		}
		if(NULL==lpdwFlags)
		{
			lpdwFlags=&dwFlags;
		}
		return WSARecv(m_sock,lpBuffers,dwBufferCount,lpdwBytesRecv,lpdwFlags,lpOL,lpCompletionRoutine);
	}

	virtual int SendEx(LPWSABUF lpBuffers,DWORD dwBufferCount,LPOVERLAPPED lpOL
		,DWORD dwFlags=0u
		,LPDWORD lpdwBytesSent=NULL		
		,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine=NULL)
	{
		ASSERT(INVALID_SOCKET!=m_sock);
		ASSERT(NULL!=lpOL);
		ASSERT(NULL!=lpBuffers);
		DWORD dwBytesSent=0;
		if(NULL==lpdwBytesSent)
		{
			lpdwBytesSent=&dwBytesSent;
		}
		return WSASend(m_sock,lpBuffers,dwBufferCount,lpdwBytesSent,dwFlags,lpOL,lpCompletionRoutine);
	}

	virtual BOOL Bind(u_long ulAddr=0u, u_short uPort=0u)
	{
		CSockAddr saAddr;
		ASSERT(INVALID_SOCKET!=m_sock);
		
		saAddr.sin_family()=AF_INET;
		saAddr.sin_port()=htons(uPort);
		saAddr.sin_addr().s_addr=ulAddr;
		int ret=bind(m_sock,saAddr,CSockAddr::namelen());
		return ret!=SOCKET_ERROR;
	}

	virtual VOID CloseSocket()
	{
		if(INVALID_SOCKET!=m_sock)
		{			
			int ret=closesocket(m_sock);
			ASSERT(0==ret);
			m_sock=INVALID_SOCKET;
		}
	}

	virtual void Attach(SOCKET &sock)
	{
		ASSERT(INVALID_SOCKET!=sock);
		m_sock=sock;		
	}

	virtual SOCKET Detach()
	{
		SOCKET ret=m_sock;
		ASSERT(INVALID_SOCKET!=m_sock);
		m_sock=INVALID_SOCKET;
		return ret;
	}

	operator SOCKET const&() const
	{
		ASSERT(INVALID_SOCKET!=m_sock);
		return m_sock;
	}

	operator HANDLE () const
	{
		ASSERT(INVALID_SOCKET!=m_sock);
		return HANDLE(m_sock);
	}
private:
	static BOOL InitFun();
protected:
	SOCKET m_sock;
	//static const int iSockaddrLen;
	static LPFN_CONNECTEX lpConnectEx;
};


__declspec(selectany) LPFN_CONNECTEX CASYNCSocket::lpConnectEx=NULL;
static CASYNCSocket InitSocket;

inline BOOL CASYNCSocket::InitFun()
{
	if(NULL!=lpConnectEx)
	{
		return TRUE;
	}
	WSAData wsaData;
	WSAStartup(MAKEWORD(2,2),&wsaData);
	static GUID guidConnectex=WSAID_CONNECTEX;
	static DWORD dwByteReturned;
	BOOL bSuccessCreate=InitSocket.CreateSocket();


	ASSERT(TRUE==bSuccessCreate);
	if(!bSuccessCreate)
	{
		return FALSE;
	}

	WSAIoctl(SOCKET(InitSocket),
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&guidConnectex,
		sizeof(GUID),
		&lpConnectEx,
		sizeof(lpConnectEx),
		&dwByteReturned,
		NULL,
		NULL
		);
	return NULL!=lpConnectEx;
}